﻿using Futajma.MigrationStudio.DataDefinition;
using Futajma.MigrationStudio.GenericImporter;
using Futajma.MigrationStudio.GenericPersistance;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;

namespace Futajma.MigrationStudio.LiveLinkExporter
{
    internal class Randomizer
    {
        public Random a = new Random(DateTime.Now.Ticks.GetHashCode());
        public List<int> randomList = new List<int>();
        

        internal int NewNumber()
        {
            bool newNumber = false;
            int number = 0;
            while (!newNumber)
            {
                number = a.Next(1000, 100000000);
                if (!randomList.Contains(number))
                {
                    randomList.Add(number);
                    newNumber = true;
                }
            }
            return number;
        }
    }

    internal class Exporter
    {
        public event ProcessUpdate ProcessUpdate;

        //Random _rnd = new Random();
        Randomizer _randomizer = new Randomizer();

        SeparateFileLoader _separateFileLoader = null;
        ProjectParameter _projectParameter;

        //string _origExportFolderPath, _origFileFolderPath, 
        string _timeStamp; //, _newStructureFolderPath;

        public Exporter(ProjectParameter projectParameter, string timestamp)
        {
            _projectParameter = projectParameter;
            _timeStamp = timestamp;
            _separateFileLoader = new SeparateFileLoader(_timeStamp);
            _separateFileLoader.ProcessUpdate += _separateFileLoader_ProcessUpdate;
        }

        void _separateFileLoader_ProcessUpdate(object sender, Update update)
        {
            InternalProcessUpdate(update);
        }

        public void Export(string origExportFile, TreeView tree, bool inclusiveEmptyFolder)
        {
            var folderPath = Path.GetDirectoryName(origExportFile);

            //_origExportFolderPath = origExportFolderPath;
            //_newStructureFolderPath = newStructureFolderPath;
            //_origFileFolderPath = origFileFolderPath;

            //XDocument origExportFile = null;

            XmlReaderSettings xmlReaderSettings = new XmlReaderSettings { CheckCharacters = false };

            using (XmlReader xmlReader = XmlReader.Create(origExportFile, xmlReaderSettings))
            {
                xmlReader.MoveToContent();
                XDocument xdocOrigExportData = XDocument.Load(xmlReader);
                XDocument xdocNewExportData = new XDocument(xdocOrigExportData);

                InternalProcessUpdate(new Update() { Information = "Load original tdx file", MessageType = MessageType.Info });
                xdocNewExportData.Descendants("folders").Remove();

                var folders = new XElement("folders");

                xdocNewExportData.Root.Add(folders);

                _separateFileLoader.LoadFiles(folderPath, GetAllExportSeparateFiles(xdocOrigExportData));

                int exportVersion = _projectParameter.ProjectVersion;

                if (_projectParameter.ProjectIsCommited)
                {
                    exportVersion = _projectParameter.ProjectVersion - 1;
                }

                foreach (TreeNode child in tree.Nodes[0].Nodes)
                {
                    CreateSubtree(child, xdocOrigExportData, folders, exportVersion, inclusiveEmptyFolder);
                }

                XmlWriterSettings settings = new XmlWriterSettings();
                //settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
                settings.OmitXmlDeclaration = true;
                settings.CheckCharacters = false;
                settings.Indent = true;

                using (XmlWriter xw = XmlWriter.Create(string.Format(@"{0}\exportData_{1}.tdx", folderPath, _timeStamp), settings))
                {
                    xdocNewExportData.Save(xw);
                }
                //xdocNewExportData.Save(string.Format(@"{0}\exportData_{1}.tdx", folderPath, _timeStamp));
                _separateFileLoader.SaveFiles(folderPath);
            }
        }

        string GetNodePath(TreeNode node)
        {
            var data = node.Tag as DataNode;

            if (node.Parent != null && data.SharePointType != SharePointType.Application
                && data.SharePointType != SharePointType.SiteCollection)
            {
                var str = GetNodePath(node.Parent);

                if (string.IsNullOrEmpty(str))
                {
                    return data.Title;
                }
                else
                {
                    return string.Format(@"{0}\{1}", GetNodePath(node.Parent), data.Title);
                }
            }
            else
            {
                return string.Empty;
            }
        }

        void InternalProcessUpdate(Update update)
        {
            if (ProcessUpdate != null)
            {
                ProcessUpdate(this, update);
            }
        }

        string[] GetAllExportSeparateFiles(XDocument exportDoc)
        {
            var files = new Dictionary<string, string>();

            var folders = exportDoc.Descendants("folder");

            foreach (var folder in folders)
            {
                var item = folder.Attribute("itemsFile");
                if (item != null)
                {
                    var itemFile = item.Value;

                    if (!files.ContainsKey(itemFile))
                    {
                        files.Add(itemFile, itemFile);
                    }
                }
            }
            return files.Keys.ToArray();
        }

        // prepare item or document for export
        private bool ProcessItem(TreeNode treeNode, XContainer xdocNewExportData, int projectVersion)
        {
            DataNode dataNode = treeNode.Tag as DataNode;
            bool itemAdded = false;

            if (dataNode.SharePointType != SharePointType.Item && dataNode.SharePointType != SharePointType.Document)
            {
                return false;
            }

            if (dataNode.Version == projectVersion)
            {
                var ssd = "";
            }

            if (string.IsNullOrEmpty(dataNode.SourceId) || dataNode.Version != projectVersion)
            {
                return false;
            }

            var docList = _separateFileLoader[dataNode.SourceId];
            var folder = xdocNewExportData.Parent;
            var href = (string)folder.Attribute("href");

            foreach (var doc in docList)
            {
                var title = (string)doc.Attribute("name");
                title = System.Web.HttpUtility.UrlDecode(title);

                InternalProcessUpdate(new Update() { Information = string.Format("Process Document {0} with id {1} to filelist {2}", dataNode.SourceId, title, _separateFileLoader.CurrentFileName), MessageType = MessageType.Info });

                if (dataNode.Title != title)
                {
                    InternalProcessUpdate(new Update() { Information = string.Format("Rename Document {0} to {1}", title, dataNode.Title), MessageType = MessageType.Info });
                    doc.Attribute("name").Value = System.Web.HttpUtility.UrlEncode(dataNode.Title);

                    doc.Descendants("property").First(x => (string)x.Attribute("ref") == "title").Value = dataNode.Title;
                    if (doc.Descendants("property").FirstOrDefault(x => (string)x.Attribute("ref") == "filename") != null)
                    {
                        doc.Descendants("property")
                            .First(x => (string)x.Attribute("ref") == "filename").Value = dataNode.Title;
                    }
                }

                doc.Attribute("folderRef").Value = href;

                var parentDataNode = treeNode.Parent.Tag as DataNode;

                doc.Descendants("property")
                .First(x => (string)x.Attribute("ref") == "parentFolderName").Value = parentDataNode.Title;

                InternalProcessUpdate(new Update() { Information = string.Format("Add Document with id {0} to filelist {1}", title, _separateFileLoader.CurrentFileName), MessageType = MessageType.Info });

                var targetpath = new XElement("property");
                targetpath.Add(new XAttribute("ref", "targetPath"));
                targetpath.Value = GetNodePath(treeNode);
                doc.Element("properties").Add(targetpath);

                _separateFileLoader.AddToFile(doc);
                itemAdded = true;
            }
            return itemAdded;
        }

        int GetFolderWorkspaceItemType(DataNode dataNode)
        {
            if (dataNode.SharePointType == SharePointType.SiteCollection || dataNode.SharePointType == SharePointType.Site)
            {
                return 202;
            }
            else
            {
                return 0;
            }
        }

        // prepare folder and sub items, subfolders for export
        private bool CreateSubtree(TreeNode treeNode, XDocument xdocOrigExportData, XContainer xdocNewExportData, int projectVersion, bool inclusiveEmptyFolder)
        {
            DataNode dataNode = treeNode.Tag as DataNode;
            XElement newElement = null;
            bool itemAdded = false;

            if (dataNode.SharePointType != SharePointType.Item &&
                dataNode.SharePointType != SharePointType.Document)
            {
                #region Folder
                _separateFileLoader.OpenNewFolder();

                if (!string.IsNullOrEmpty(dataNode.SourceId))
                {
                    #region existing Folder from Shareweb
                    // LL Object
                    InternalProcessUpdate(new Update() { Information = string.Format("Process folder {0} with Id {1}", dataNode.Title, dataNode.SourceId), MessageType = MessageType.Info });
                    var el = xdocOrigExportData.Descendants("property")
                   .Where(x => (string)x.Attribute("ref") == "liveLinkId" && x.Value == dataNode.SourceId);

                    if (el != null && el.Count() > 0)
                    {
                        var folder = el.First().Parent.Parent;
                        newElement = new XElement(folder);
                        newElement.Descendants("folders").Remove();
                        var targetpath = new XElement("property");
                        targetpath.Add(new XAttribute("ref", "targetPath"));
                        targetpath.Value = GetNodePath(treeNode);
                        newElement.Element("properties").Add(targetpath);

                        newElement.Attribute("name").Value = System.Web.HttpUtility.UrlEncode(dataNode.Title);

                        if (newElement.Attribute("itemsFile") == null)
                        {
                            newElement.Add(new XAttribute("itemsFile", _separateFileLoader.CurrentFileName));
                        }
                        else
                        {
                            newElement.Attribute("itemsFile").Value = _separateFileLoader.CurrentFileName;
                        }

                        newElement.Descendants("property")
                       .First(x => (string)x.Attribute("ref") == "title").Value = dataNode.Title;

                        newElement.Descendants("property")
                        .First(x => (string)x.Attribute("ref") == "itemType").Value = GetFolderWorkspaceItemType(dataNode).ToString();

                    }
                    else
                    {
                        return false;
                    }

                    #endregion
                }
                else
                {
                    #region in SP-tree created folder or library

                    // create emnpty LL object

                    var newNumber = false;
                    int random = 0;

                    while (!newNumber)
                    {
                        random = _randomizer.NewNumber();

                        // check if already exisis
                        if (xdocOrigExportData.Descendants("property").Where(x => (string)x.Attribute("ref") == "liveLinkId" && x.Value == random.ToString()).Count() == 0)
                        {
                            InternalProcessUpdate(new Update() { Information = string.Format("New folder created. For {0} with Id {1}", dataNode.Title, random), MessageType = MessageType.Info });
                            newNumber = true;
                        }
                    }

                    var name = System.Web.HttpUtility.UrlEncode(dataNode.Title);

                    var topFolder = xdocOrigExportData.Descendants("folder").First();

                    newElement = new XElement(topFolder);
                    newElement.Descendants("folders").Remove();
                    newElement.Attribute("name").Value = name;
                    var sourceUrl = string.IsNullOrEmpty(dataNode.SourceUrl) ? string.Format("{0}{1}", (string)newElement.Attribute("href"), random.ToString()) : dataNode.SourceUrl;
                    newElement.Attribute("href").Value = sourceUrl;

                    if (newElement.Attribute("itemsFile") == null)
                    {
                        newElement.Add(new XAttribute("itemsFile", _separateFileLoader.CurrentFileName));
                    }
                    else
                    {
                        newElement.Attribute("itemsFile").Value = _separateFileLoader.CurrentFileName;
                    }

                    var targetpath = new XElement("property");
                    targetpath.Add(new XAttribute("ref", "targetPath"));
                    targetpath.Value = GetNodePath(treeNode);
                    newElement.Element("properties").Add(targetpath);

                    newElement.Descendants("property")
                   .First(x => (string)x.Attribute("ref") == "title").Value = dataNode.Title;
                    newElement.Descendants("property")
                  .First(x => (string)x.Attribute("ref") == "liveLinkId").Value = random.ToString();

                    newElement.Descendants("property")
                   .First(x => (string)x.Attribute("ref") == "itemType").Value = GetFolderWorkspaceItemType(dataNode).ToString();

                    // update nodes in tree with generated Id and href
                    //dataNode.SourceId = random.ToString();
                    dataNode.SourcePath = sourceUrl;

                    #endregion
                }

                bool subItemAdded = false;

                if (treeNode.Nodes.Count > 0)
                {
                    var folders = new XElement("folders");
                    newElement.Add(folders);
                    // process all items in folder
                    foreach (TreeNode child in treeNode.Nodes)
                    {
                        if (ProcessItem(child, folders, projectVersion))
                        {
                            subItemAdded = true;
                        }
                    }
                    // process subfolders in folder
                    foreach (TreeNode child in treeNode.Nodes)
                    {
                        if (CreateSubtree(child, xdocOrigExportData, folders, projectVersion, inclusiveEmptyFolder))
                        {
                            subItemAdded = true;
                        }
                    }
                }

                if (inclusiveEmptyFolder || subItemAdded)
                {
                    xdocNewExportData.Add(newElement);
                    itemAdded = true;
                }
            }
                #endregion
            return itemAdded;
        }


        public string GetName()
        {
            return "Tzunami tdx Exporter";
        }

        public string GetDescription()
        {
            return "Converts the target tree into tzunami deployer usabel structure";
        }
    }
}